package fr.iut.tp3.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;

import fr.iut.tp3.model.UtilisateurImpl.Privilege;
import fr.iut.tp3.utils.StringUtils;

//extends AbstractTableModel
public class ListeUtilisateursImpl implements ListeUtilisateurs {

	private static final int NB_Column = 4;

	private ArrayList<Utilisateur> utilisateurs = new ArrayList<Utilisateur>();

	private List<TableModelListener> tableListenerList = new ArrayList<TableModelListener>();

	private List<TreeModelListener> treeListenerList = new ArrayList<TreeModelListener>();

	@Override
	public void ajouterUtilisateur(Utilisateur u) {
		utilisateurs.add(u);
		fireTreeNodeInserted(u);
		fireTableRowsInserted(utilisateurs.size() - 1, utilisateurs.size());
	}

	@Override
	public void ajouterUtilisateur(String nom, String login, String password, Integer niveauPrivilege, String loginSuperieur) {
		Utilisateur user = new UtilisateurImpl(nom, login, password, niveauPrivilege, loginSuperieur);
		utilisateurs.add(user);
		fireTableRowsInserted(utilisateurs.size() - 1, utilisateurs.size());
	}

	private void fireTreeNodeInserted(Utilisateur user) {
		List<Utilisateur> userPath = getUserTreePath(user);

		// Création de l'événement.
		TreePath treePath = new TreePath(userPath.toArray());
		int[] childIndices = {};
		Utilisateur[] children = {user};
		TreeModelEvent event = new TreeModelEvent(this, treePath, childIndices, children);

		// Déclenchement des listeners.
		for (TreeModelListener t : treeListenerList) {
			t.treeNodesInserted(event);
		}
	}

	private List<Utilisateur> getUserTreePath(Utilisateur user) {
		List<Utilisateur> userPath = new ArrayList<Utilisateur>();
		// On ajoute l'utilisateur et on récupère son parent.
		Utilisateur parent = getUtilisateurFromLogin(user.getLoginSuperieurHierarchique());
		if (parent != null) {
			// S'il a un parent, on l'ajoute, et on cherche les autres parents, jusqu'au dernier.
			userPath.add(parent);
			while (!StringUtils.isBlank(parent.getLoginSuperieurHierarchique())) {
				parent = getUtilisateurFromLogin(parent.getLoginSuperieurHierarchique());
				userPath.add(parent);
			}
		}
		// On inverse l'ordre de la liste, pour avoir un vrai TreePath.
		Collections.reverse(userPath);
		return userPath;
	}

	private Utilisateur getUtilisateurFromLogin(String login) {
		for (Utilisateur u : utilisateurs) {
			if (u.getLogin().equals(login)) {
				return u;
			}
		}
		return null;
	}

	@Override
	public Utilisateur obtenirUtilisateur(int numeroLigne) {		
		return utilisateurs.get(numeroLigne);
	}

	@Override
	public void supprimerUtilisateur(int numeroLigne) {
		utilisateurs.remove(numeroLigne);
		fireTableRowsDeleted(numeroLigne, numeroLigne);
		fireTreeUserDeleted(obtenirUtilisateur(numeroLigne));
	}

	private void fireTreeUserDeleted(Utilisateur user) {
		List<Utilisateur> userPath = getUserTreePath(user);

		// Création de l'événement.
		TreePath treePath = new TreePath(userPath.toArray());
		int[] childIndices = {getIndexOfChild(getUtilisateurFromLogin(user.getLoginSuperieurHierarchique()), user)};
		Utilisateur[] children = {user};
		TreeModelEvent event = new TreeModelEvent(this, treePath, childIndices, children);

		// Déclenchement des listeners.
		for (TreeModelListener t : treeListenerList) {
			t.treeNodesInserted(event);
		}
	}

	@Override
	public int obtenirNumeroLigneUtilisateur(String login) {
		if (StringUtils.isBlank(login)) {
			return -1;
		}
		for (Utilisateur u : utilisateurs) {
			if (u.getLogin().equals(login)) {
				return utilisateurs.indexOf(u);
			}
		}
		return -1;
	}

	@Override
	public void modifierUtilisateur(int ligne, Utilisateur u) {
		utilisateurs.set(ligne, u);
		fireTableRowsUpdated(ligne, ligne);
	}

	@Override
	public boolean verifierIdentite(String login, String password) {
		if (StringUtils.isBlank(password) || StringUtils.isBlank(login)) {
			return false;
		}
		for (Utilisateur u : utilisateurs) {
			if (u.getLogin().equals(login)) {
				return u.checkPassword(password);
			}
		}
		return false;
	}

	@Override
	public int nbUtilisateurs() {
		return utilisateurs.size();
	}

	public String[] obtenirSuperieurs() {
		String[] tabSup = new String[utilisateurs.size()];
		for (int id = 0; id < utilisateurs.size(); id++) {
			tabSup[id] = utilisateurs.get(id).getLogin();
		}
		return tabSup;
	}

	public String[] obtenirLogins() {
		String[] tabSup = new String[utilisateurs.size()];
		for (int id = 0; id < utilisateurs.size(); id++) {
			tabSup[id] = utilisateurs.get(id).getLogin();
		}
		return tabSup;
	}

	@Override
	public int getColumnCount() {
		return NB_Column;
	}

	@Override
	public int getRowCount() {
		return nbUtilisateurs();
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		if (rowIndex < utilisateurs.size()) {
			Utilisateur utilisateur = utilisateurs.get(rowIndex);
			switch (columnIndex) {
			case 0:
				return utilisateur.getNom();
			case 1:
				return utilisateur.getLogin();
			case 2:
				return utilisateur.getNiveauPrivilege();
			case 3:
				return utilisateur.getLoginSuperieurHierarchique();
			default:
				break;
			}
		}
		return null;
	}

	@Override
	public String getColumnName(int name) {
		switch (name) {
		case 0:
			return Colonne.NOM.getLabel();
		case 1:
			return Colonne.LOGIN.getLabel();
		case 2:
			return Colonne.NIV_PRIVILEGE.getLabel();
		case 3:
			return Colonne.SUPERIEUR.getLabel();
		default:
			return "";
		}
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		// colonne login
		if (columnIndex == 1) {
			return false;
		} else {
			return true;
		}

	}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		if (rowIndex < utilisateurs.size()) {
			Utilisateur utilisateur = utilisateurs.get(rowIndex);
			switch (columnIndex) {
			case 0:
				utilisateur.setNom((String) aValue);
				break;
			case 1:
				utilisateur.setLogin((String) aValue);
				break;
			case 2:
				String tmp = aValue.toString();
				try {
					utilisateur.setNiveauPrivilege(Integer.parseInt(tmp));
				} catch (java.lang.NumberFormatException e) {
					if (tmp.equals(Privilege.ADMIN.getLabel())) {
						utilisateur.setNiveauPrivilege(Privilege.ADMIN.getValeur());
					} else if (tmp.equals(Privilege.UTILISATEUR.getLabel())) {
						utilisateur.setNiveauPrivilege(Privilege.UTILISATEUR.getValeur());
					} else if (tmp.equals(Privilege.INVITE.getLabel())) {
						utilisateur.setNiveauPrivilege(Privilege.INVITE.getValeur());
					}

				}
				break;
			case 3:
				utilisateur.setLoginSuperieurHierarchique((String) aValue);
				break;
			default:
				break;
			}
			fireTableRowsUpdated(rowIndex, rowIndex);
		}
	}

	@Override
	public void addTableModelListener(TableModelListener listener) {
		tableListenerList.add(listener);
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		switch (columnIndex) {
		case 0:
			return String.class;
		case 1:
			return String.class;
		case 2:
			return Integer.class;
		case 3:
			return String.class;
		default:
			break;
		}
		return null;
	}

	@Override
	public void removeTableModelListener(TableModelListener listener) {
		tableListenerList.remove(listener);
	}

	/**
	 * Notifies all listeners that rows in the range [firstRow, lastRow],
	 * inclusive, have been updated.
	 * 
	 * @param firstRow
	 * @param lastRow
	 */
	private void fireTableRowsUpdated(int firstRow, int lastRow) {
		TableModelEvent tableModelEvent = new TableModelEvent(this, firstRow,lastRow);
		for (TableModelListener t : tableListenerList) {
			t.tableChanged(tableModelEvent);
		}
	}

	/**
	 * Notifies all listeners that rows in the range [firstRow, lastRow],
	 * inclusive, have been inserted.
	 * 
	 * @param firstRow
	 * @param lastRow
	 */
	private void fireTableRowsInserted(int firstRow, int lastRow) {
		TableModelEvent tableModelEvent = new TableModelEvent(this, firstRow,
				lastRow, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT);
		for (TableModelListener t : tableListenerList) {
			t.tableChanged(tableModelEvent);
		}
	}

	/**
	 * Notifies all listeners that rows in the range [firstRow, lastRow],
	 * inclusive, have been deleted.
	 * 
	 * @param firstRow
	 * @param lastRow
	 */
	private void fireTableRowsDeleted(int firstRow, int lastRow) {
		TableModelEvent tableModelEvent = new TableModelEvent(this, firstRow,
				lastRow, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE);
		for (TableModelListener t : tableListenerList) {
			t.tableChanged(tableModelEvent);
		}
	}

	@Override
	public void addTreeModelListener(TreeModelListener l) {
		treeListenerList.add(l);
	}

	@Override
	public Object getChild(Object parent, int index) {
		List<Utilisateur> children = getChildren(parent);
		return children.get(index);
	}

	@Override
	public int getChildCount(Object parent) {
		return getChildren(parent).size();
	}

	@Override
	public int getIndexOfChild(Object parent, Object child) {
		Utilisateur userChild = (Utilisateur) child;
		List<Utilisateur> children = getChildren(parent);
		int index = 0;
		for (Utilisateur u : children) {
			if (u.equals(userChild)) {
				break;
			}
			index++;
		}
		return index;
	}

	@Override
	public Object getRoot() {
		for (Utilisateur u : utilisateurs) {
			if (StringUtils.isBlank(u.getLoginSuperieurHierarchique())) {
				return u;
			}
		}
		return null;
	}

	@Override
	public boolean isLeaf(Object node) {
		Utilisateur user = (Utilisateur) node;
		for (Utilisateur u : utilisateurs) {
			if (u.getLoginSuperieurHierarchique().equals(user.getLogin())) {
				return false;
			}
		}
		return true;
	}

	@Override
	public void removeTreeModelListener(TreeModelListener l) {
		treeListenerList.remove(l);
	}

	@Override
	public void valueForPathChanged(TreePath path, Object newValue) {
		Object node = path.getLastPathComponent();
		node = newValue;
	}

	private List<Utilisateur> getChildren(Object parent) {
		Utilisateur userParent = (Utilisateur) parent;
		List<Utilisateur> children = new ArrayList<Utilisateur>();
		for (Utilisateur u : utilisateurs) {
			if (u.getLoginSuperieurHierarchique().equals(userParent.getLogin())) {
				children.add(u);
			}
		}
		return children;
	}

	public enum Colonne {
		NOM("Admin", 0), LOGIN("Login", 1), NIV_PRIVILEGE("Niveau", 2), SUPERIEUR("Supérieur", 3);

		private String label = "";
		public final Integer valeur;

		// Constructeur
		Colonne(String label, Integer valeur) {
			this.label = label;
			this.valeur = valeur;
		}

		public String getLabel() {
			return label;
		}

		public Integer getValeur() {
			return valeur;
		}
	}
}
